// =============================================================================
//  STATS - Statistical Analysis Tools, v.0.01
//  Copyright (C) 2011  Marco Vettigli
// -----------------------------------------------------------------------------
//
// This program  is free software:  you can  redistribute it and/or  modify it
// under the terms of the  GNU General Public License as published by the Free
// Software Foundation,  either version 3 of the License,  or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY;  without even  the  implied  warranty  of  MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the  GNU General Public License  for
// more details.
//
// You  should  have received a copy of the  GNU General Public License  along
// with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// =============================================================================
//
// STABLE.H
// The class defines a model to store data in tables; it is the common data
// source for all tabular data management in StatS.
//
// Author: Marco Vettigli
//
// =============================================================================

#ifndef STABLE_H
#define STABLE_H

// == include directives =======================================================

#include <QAbstractTableModel>
#include <QVector>
#include <QFileInfo>
#include "sdoubles.h"
#include "sstrings.h"
#include "scolproperty.h"
#include "srowproperty.h"

// == class definitions ========================================================

namespace StatS
{

class STable : public QAbstractTableModel
{

     Q_OBJECT

public:

// == constructors and destructors =============================================

    // -- public ---------------------------------------------------------------
    // StatS::STable::Stable(const QString)
    //
    // Default constructor for STable class, it builds a new STable object with
    // given name 'name'. If 'name' is null or not provided, the object will be
    // named as "Untitled" by default.
    //
    // -------------------------------------------------------------------------
    STable(const QString name = "Untitled");

// == getters ==================================================================

    // -- public ---------------------------------------------------------------
    // StatS::STable::name()
    //
    // Returns the name of the STable object.
    //
    // -------------------------------------------------------------------------
    QString name() const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::untitledColumnName()
    //
    // Returns the current untitled column name without updating the inner
    // untitled index. The index is updated only upon table modification or
    // column renaming.
    //
    // -------------------------------------------------------------------------
    const QString untitledColumnName();

    // -- public ---------------------------------------------------------------
    // StatS::STable::rowCount(const QModelIndex&)
    //
    // Returns number of rows in the STable object.
    //
    // -------------------------------------------------------------------------
    virtual int rowCount(const QModelIndex& /*parent*/ = QModelIndex()) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::columnCount(const QModelIndex&)
    //
    // Returns number of columns in the STable object.
    //
    // -------------------------------------------------------------------------
    virtual int columnCount(const QModelIndex& /*parent*/ = QModelIndex())
    const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::flags(const QModelIndex&)
    //
    // Returns the item flags for the given index.
    //
    // -------------------------------------------------------------------------
    virtual Qt::ItemFlags flags(const QModelIndex& index) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::data(const QModelIndex&, int)
    //
    // Returns the data stored under the given role 'role' for the item
    // referred to by the index 'index'.
    //
    // -------------------------------------------------------------------------
    virtual QVariant data(const QModelIndex &index,
                          int role = Qt::DisplayRole) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::dataString(int, int)
    //
    // The function returns a QString of data indexed by 'row' and 'col'
    // numbers. If data is null (both of type SDoubles and SStrings) a null
    // QString is returned.
    //
    // -------------------------------------------------------------------------
    QString dataString(int row, int col) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::headerData(const QModelIndex&, Qt::Orientation, int)
    //
    // Returns the data for the given role 'role' and index 'section' in the
    // header with the specified orientation. For horizontal headers, the
    // 'section' number corresponds to the column number. Similarly, for
    // vertical headers, the 'section' number corresponds to the row number.
    //
    // -------------------------------------------------------------------------
    virtual QVariant headerData(int section,
                                Qt::Orientation orientation,
                                int role = Qt::DisplayRole) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::getColumnName(int)
    //
    // Returns the name of the column indexed by number 'col'.
    //
    // -------------------------------------------------------------------------
    QString getColumnName(int col) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::fileName()
    //
    // Returns the name of the file associated with the STable object. When
    // save() operation is requested, table data will be saved in this file.
    //
    // -------------------------------------------------------------------------
    QString fileName() const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::getColumnType(int)
    //
    // Returns the array type (as SColProperty::ArrayType) at given column
    // 'col' index, 'Undefined' if column index is not valid.
    //
    // -------------------------------------------------------------------------
    StatS::SColProperty::ArrayType getColumnType(int col) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::getColumnProperty(int)
    //
    // Returns a constant reference to the SColProperty indexed by 'col' number.
    //
    // -------------------------------------------------------------------------
    const StatS::SColProperty* getColumnProperty(int col) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::getRowProperty(int)
    //
    // Returns a constant reference to the SRowProperty indexed by 'row' number.
    //
    // -------------------------------------------------------------------------
    const StatS::SRowProperty* getRowProperty(int row) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::getColumnReference(int)
    //
    // Returns a void reference to the array indexed by 'col' number.
    //
    // -------------------------------------------------------------------------
    void* getColumnReference(int col);

    // -- public ---------------------------------------------------------------
    // StatS::STable::getColumnReference(int)
    //
    // Returns the column number referred to array 'array' stored in the
    // STable. If it is not present, -1 is returned
    //
    // -------------------------------------------------------------------------
    int getColumnIndex(void* array) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::getColumnReference(const QString)
    //
    // Returns the column number referred to column name 'name' stored in the
    // STable. If it is not present, -1 is returned
    //
    // -------------------------------------------------------------------------
    int getColumnIndex(const QString name) const;

// == setters ==================================================================

    // -- public ---------------------------------------------------------------
    // StatS::STable::setName(const QString)
    //
    // The function renames the STable object with 'name' argument. If provided
    // name is null, renaming is not successful and false is returned.
    //
    // -------------------------------------------------------------------------
    bool setName(const QString name);

    // -- public ---------------------------------------------------------------
    // StatS::STable::setName(const QString)
    //
    // The function redefines the file associated with STable object by
    // 'filename' argument.
    //
    // -------------------------------------------------------------------------
    bool setFilename(const QString filename);

    // -- public ---------------------------------------------------------------
    // StatS::STable::setData(const QModelIndex, const QVariant, int)
    //
    // The function assigns data 'value' under a given 'role' of item indexed
    // by 'index'.
    //
    // -------------------------------------------------------------------------
    virtual bool setData(const QModelIndex &index,
                         const QVariant &value,
                         int role = Qt::EditRole);

    // -- public ---------------------------------------------------------------
    // StatS::STable::setHeaderData(const QModelIndex&, Qt::Orientation,
    //                              const QVariant, int)
    //
    // The function assign data for the given role 'role' and index 'section'
    // in the header with the specified orientation. For horizontal headers,
    // the 'section' number corresponds to the column number. Similarly, for
    // vertical headers, the 'section' number corresponds to the row number.
    //
    // -------------------------------------------------------------------------
    virtual bool setHeaderData(int section,
                               Qt::Orientation orientation,
                               const QVariant &value,
                               int role = Qt::EditRole);

    // -- public ---------------------------------------------------------------
    // StatS::STable::setRowMarker(int, SRowProperty::RowMarker)
    //
    // The function sets row marker type to row indexed by 'row'.
    //
    // -------------------------------------------------------------------------
    bool setRowMarker(int row,
                      StatS::SRowProperty::RowMarker marker);

    // -- public ---------------------------------------------------------------
    // StatS::STable::setRowColor(int, const QColor)
    //
    // The function sets row color to row indexed by 'row'.
    //
    // -------------------------------------------------------------------------
    bool setRowColor(int row,
                     const QColor color);

    // -- public ---------------------------------------------------------------
    // StatS::STable::setRowSize(int, int)
    //
    // The function sets row size 'size' to row indexed by 'row'.
    //
    // -------------------------------------------------------------------------
    bool setRowSize(int row,
                    int size = 3);

    // -- public ---------------------------------------------------------------
    // StatS::STable::setColumnDescription(int, const QString)
    //
    // The function sets column description to column indexed by 'col'.
    //
    // -------------------------------------------------------------------------
    bool setColumnDescription(int col,
                              const QString description);

    // -- public ---------------------------------------------------------------
    // StatS::STable::setColumnUnits(int, const QString)
    //
    // The function sets column units to column indexed by 'col'.
    //
    // -------------------------------------------------------------------------
    bool setColumnUnits(int col,
                        const QString units);

    // -- public ---------------------------------------------------------------
    // StatS::STable::setColumnUsl(int, double)
    //
    // The function sets upper spec limit to column indexed by 'col'.
    //
    // -------------------------------------------------------------------------
    bool setColumnUsl(int col,
                      double usl);

    // -- public ---------------------------------------------------------------
    // StatS::STable::setColumnLsl(int, double)
    //
    // The function sets lower spec limit to column indexed by 'col'.
    //
    // -------------------------------------------------------------------------
    bool setColumnLsl(int col,
                      double lsl);

// == inspectors ===============================================================

    // -- public ---------------------------------------------------------------
    // StatS::STable::isDataSaved()
    //
    // Returns true if table is saved on file, else false.
    //
    // -------------------------------------------------------------------------
    bool isDataSaved() const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::isColumnIndex(int)
    //
    // The function checks if number 'col' is valid as column index.
    //
    // -------------------------------------------------------------------------
    bool isColumnIndex(int col) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::isRowIndex(int)
    //
    // The function checks if number 'row' is valid as row index.
    //
    // -------------------------------------------------------------------------
    bool isRowIndex(int row) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::isColumnNameUsed(const QString)
    //
    // The function checks if 'name' is already used as column name.
    //
    // -------------------------------------------------------------------------
    bool isColumnNameUsed(const QString name) const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::isColumnConvertable(int, SColProperty::ArrayType)
    //
    // The function check if column at 'col' index is convertable to array type
    // 'type' without data loss.
    //
    // -------------------------------------------------------------------------
    bool isColumnConvertable(int col,
                             StatS::SColProperty::ArrayType type) const;

// == accessor function declarations ===========================================

    // -- public ---------------------------------------------------------------
    // StatS::STable::save()
    //
    // The fuction saves the table in the file referenced by table filename.
    // It returns true if saving was successful, else false.
    //
    // -------------------------------------------------------------------------
    bool save();

// == column accessors =========================================================

    // -- public ---------------------------------------------------------------
    // StatS::STable::convertColumn(int, SColProperty::ArrayType)
    //
    // The function converts a column indexed by 'col' in column type 'type'.
    // If a value in the column is not convertable, null value is put in place.
    // It returns true if column convertion is successful, else false.
    //
    // -------------------------------------------------------------------------
    bool convertColumn(int col,
                       StatS::SColProperty::ArrayType type);

    // -- public ---------------------------------------------------------------
    // StatS::STable::insertColumn(int, const QModelindex&)
    //
    // The function inserts a new character column at given 'col' index.
    // The number 'col' must be a valid column index or the number of column,
    // to append the column at end of the table.
    // It returns true if column insertion is successful, else false.
    //
    // -------------------------------------------------------------------------
    bool insertColumn(int col,
                      const QModelIndex& /*parent*/ = QModelIndex());

    // -- public ---------------------------------------------------------------
    // StatS::STable::insertColumn(int, SColProperty::ArrayType,
    //                             QString, const QModelindex&)
    //
    // The function inserts a new column of given 'type' at given 'col' index.
    // The new column will be renamed as 'name'; the number 'col' must be a
    // valid column index or the number of column, to append the column at end
    // of the table. It returns true if column insertion is successful, else
    // false.
    //
    // -------------------------------------------------------------------------
    bool insertColumn(int col,
                      StatS::SColProperty::ArrayType type,
                      QString name = QString(),
                      const QModelIndex& /*parent*/ = QModelIndex());

    // -- public ---------------------------------------------------------------
    // StatS::STable::insertColumns(int, int, const QModelindex&)
    //
    // The function inserts a number 'n' of new character column at given 'col'
    // index. The number 'col' must be a valid column index or the number of
    // column, to append the column at end of the table.
    // It returns true if columns insertion is successful, else false.
    //
    // -------------------------------------------------------------------------
    bool insertColumns(int col,
                       int n,
                       const QModelIndex& /*parent*/ = QModelIndex());

    // -- public ---------------------------------------------------------------
    // StatS::STable::insertColumn(int, SColProperty::ArrayType,
    //                             QString, const QModelindex&)
    //
    // The function inserts a number 'n' of new column of given 'type' at given
    // 'col' index. New columns will be renamed as 'name' followed by an
    // integer value; the number 'col' must be a valid column index or the
    // number of column, to append the column at end of the table. It returns
    // true if column insertion is successful, else false.
    //
    // -------------------------------------------------------------------------
    bool insertColumns(int col,
                       int n,
                       StatS::SColProperty::ArrayType type,
                       QString name = QString(),
                       const QModelIndex& /*parent*/ = QModelIndex());

    // -- public ---------------------------------------------------------------
    // StatS::STable::removeColumn(int, const QModelindex&)
    //
    // The function removes column at given 'col' index. The number 'col' must
    // be a valid column index. It returns true if column deletion is
    // successful, else false.
    //
    // -------------------------------------------------------------------------
    bool removeColumn(int col,
                      const QModelIndex& /*parent*/ = QModelIndex());

    // -- public ---------------------------------------------------------------
    // StatS::STable::removeColumns(int, int, const QModelindex&)
    //
    // The function removes a number 'n' of columns at given 'col' index. The
    // number 'col' must be a valid column index and deletion cannot overflow
    // column number. It returns true if columns deletion is successful, else
    // false.
    //
    // -------------------------------------------------------------------------
    bool removeColumns(int col,
                       int n,
                       const QModelIndex& /*parent*/ = QModelIndex());

// == row accessors ============================================================

    // -- public ---------------------------------------------------------------
    // StatS::STable::insertRow(int, const QModelindex&)
    //
    // The function inserts a single row at given row position 'row'.
    // For this to be successful, 'row' number must be a valid row index.
    // It returns true if row insertion is successful, else false.
    //
    // -------------------------------------------------------------------------
    bool insertRow(int row,
                   const QModelIndex& /*parent*/ = QModelIndex());

    // -- public ---------------------------------------------------------------
    // StatS::STable::insertRows(int, const QModelindex&)
    //
    // The function inserts a number 'n' of rows at given row position 'row'.
    // For this to be successful, 'row' number must be a valid row index.
    // It returns true if rows insertion is successful, else false.
    //
    // -------------------------------------------------------------------------
    bool insertRows(int row,
                    int n,
                    const QModelIndex& /*parent*/ = QModelIndex());

    // -- public ---------------------------------------------------------------
    // StatS::STable::removeRow(int, const QModelindex&)
    //
    // The function remove a row at given row position 'row'.
    // For this to be successful, 'row' number must be a valid row index.
    // It returns true if row deletion is successful, else false.
    //
    // -------------------------------------------------------------------------
    bool removeRow(int row,
                   const QModelIndex& /*parent*/ = QModelIndex());

    // -- public ---------------------------------------------------------------
    // StatS::STable::removeRows(int, int, const QModelindex&)
    //
    // The function remove a number 'n' of rows at given row position 'row'.
    // For this to be successful, 'row' number must be a valid row index and
    // deletion must not overflow table size. It returns true if rows deletion
    // is successful, else false.
    //
    // -------------------------------------------------------------------------
    bool removeRows(int row,
                    int n,
                    const QModelIndex& /*parent*/ = QModelIndex());

// == selection accessors ======================================================

    // -- public ---------------------------------------------------------------
    // StatS::STable::selectedColumns()
    //
    // Returns a list of integers containing all selected column indexes.
    //
    // -------------------------------------------------------------------------
    const QList<int> selectedColumns() const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::selectedRows()
    //
    // Returns a list of integers containing all selected row indexes.
    //
    // -------------------------------------------------------------------------
    const QList<int> selectedRows() const;

    // -- public ---------------------------------------------------------------
    // StatS::STable::selectColumn(int)
    //
    // The function selects and deselect a column indexed by 'col' number,
    // based on current selection state of column. If 'allSelect' is true,
    // the function doesn't deselect column.
    //
    // -------------------------------------------------------------------------
    bool selectColumn(int col,
                      bool allSelect = false);

    // -- public ---------------------------------------------------------------
    // StatS::STable::selectColumns(QList<int>, bool)
    //
    // The function selects and unselect a list of columns given by QList<int>
    // 'list'. If the function is called without specifying 'list' arguments,
    // all columns will be (de)selected, depending on table current state. If
    // 'allSelect' is true, the function doesn't deselect columns.
    //
    // -------------------------------------------------------------------------
    bool selectColumns(QList<int> list = QList<int>(),
                       bool allSelect = false);

    // -- public ---------------------------------------------------------------
    // StatS::STable::selectRow(int)
    //
    // The function selects and deselect a row indexed by 'row' number,
    // based on current selection state of row. If 'allSelect' is true,
    // the function doesn't deselect row.
    //
    // -------------------------------------------------------------------------
    bool selectRow(int row,
                   bool allSelect = false);

    // -- public ---------------------------------------------------------------
    // StatS::STable::selectRows(QList<int>, bool)
    //
    // The function selects and deselect a list of rows given by QList<int>
    // 'list'. If the function is called without specifying 'list' arguments,
    // all rows will be (de)selected, depending on table current state. If
    // 'allSelect' is true, the function doesn't deselect rows.
    //
    // -------------------------------------------------------------------------
    bool selectRows(QList<int> list = QList<int>(),
                    bool allSelect = false);


    // -- public ---------------------------------------------------------------
    // StatS::STable::clearColumnSelection()
    //
    // The function clear column selection.
    //
    // -------------------------------------------------------------------------
    void clearColumnSelection();

    // -- public ---------------------------------------------------------------
    // StatS::STable::clearRowSelection()
    //
    // The function clear row selection.
    //
    // -------------------------------------------------------------------------
    void clearRowSelection();

    // -- public ---------------------------------------------------------------
    // StatS::STable::clearSelection()
    //
    // The function clear row and column selection.
    //
    // -------------------------------------------------------------------------
    void clearSelection();

// == variable declarations ====================================================

    // defines default number of rows when STable is first created
    const static int DEFAULT_ROWS = 10;

    // defines default number of columns when STable is first created
    const static int DEFAULT_COLS = 1;

private:

    QVector<SColProperty> _colsProperties;      // vector of column properties
    QVector<SRowProperty> _rowsProperties;      // vector of column properties

    QString _name;              // table name
    QString _filename;          // table filename
    bool _savedState;           // define the save state of the table
    int _cols;                  // number of columns
    int _rows;                  // number of rows
    int _untitledNumber;        // untitled name counter

// == private function declarations ============================================

    // -- private --------------------------------------------------------------
    // StatS::STable::parsedString(QString)
    //
    // The function formats the string 'data' to be stored in a file.
    //
    // -------------------------------------------------------------------------
    QString parsedString(QString data) const;

};

}

#endif // STABLE_H
